#include	"compiler.h"
#include	"resize.h"


enum {
	RESIZE_HDIF		= 0x01,
	RESIZE_VDIF		= 0x02
};

enum {
	MXBITS		= 8,
	MYBITS		= 8,
	MULTIX		= (1 << MXBITS),
	MULTIY		= (1 << MYBITS),
	MXMASK		= (MULTIX - 1),
	MYMASK		= (MULTIY - 1)
};

enum {
	B16BIT		= 5,
	G16BIT		= 6,
	R16BIT		= 5,
	B16SFT		= 0,
	G16SFT		= 5,
	R16SFT		= 11
};

#define	GET16B(c)	(((c) >> B16SFT) & ((1 << B16BIT) - 1))
#define	GET16G(c)	(((c) >> G16SFT) & ((1 << G16BIT) - 1))
#define	GET16R(c)	(((c) >> R16SFT) & ((1 << R16BIT) - 1))

typedef struct {
	RSZFN	func;
	int		width;
	int		height;

	int		orgx;
	int		orgy;
	UINT	*h;
	UINT	*buf;
	UINT	bufsize;
} _RSZEX, *RSZEX;



// ---- convert sub

typedef void (*FNCNV)(RSZHDL hdl, BYTE *dst, const BYTE *src);

#if defined(RESIZE_FASTCOPY) || defined(RESIZE_BILINEAR)
static void cc16by24(RSZHDL hdl, BYTE *dst, const BYTE *src) {

	UINT	width;
	UINT	col;

	width = hdl->width;
	do {
		col = (src[0] >> (8 - B16BIT)) << B16SFT;
		col += (src[1] >> (8 - G16BIT)) << G16SFT;
		col += (src[2] >> (8 - R16BIT)) << R16SFT;
		*(UINT16 *)dst = (UINT16)col;
		src += 3;
		dst += 2;
	} while(--width);
}

static void cc24by16(RSZHDL hdl, BYTE *dst, const BYTE *src) {

	UINT	width;
	UINT	col;
	UINT	tmp;

	width = hdl->width;
	do {
		col = *(UINT16 *)src;
		tmp = (col >> B16SFT) & ((1 << B16BIT) - 1);
		dst[0] = (BYTE)((tmp << (8 - B16BIT)) + (tmp >> (B16BIT * 2 - 8)));
		tmp = (col >> G16SFT) & ((1 << G16BIT) - 1);
		dst[1] = (BYTE)((tmp << (8 - G16BIT)) + (tmp >> (G16BIT * 2 - 8)));
		tmp = (col >> R16SFT) & ((1 << R16BIT) - 1);
		dst[2] = (BYTE)((tmp << (8 - R16BIT)) + (tmp >> (R16BIT * 2 - 8)));
		src += 2;
		dst += 3;
	} while(--width);
}
#endif

#if defined(RESIZE_FASTCOPY)
static void cc8(RSZHDL hdl, BYTE *dst, const BYTE *src) {

	CopyMemory(dst, src, hdl->width);
}

static void cc16(RSZHDL hdl, BYTE *dst, const BYTE *src) {

	CopyMemory(dst, src, hdl->width * 2);
}

static void cc24(RSZHDL hdl, BYTE *dst, const BYTE *src) {

	CopyMemory(dst, src, hdl->width * 3);
}

static const FNCNV cnvcpy[RSZFNMAX] = {cc8, cc16, cc24, cc16by24, cc24by16};
#endif



// ----

#if defined(RESIZE_FASTCOPY)
static void fastcopyfunc(RSZHDL hdl, UINT type, BYTE *dst, int dalign,
												const BYTE *src, int salign) {

	UINT	height;
	FNCNV	cnv;

	if (type >= RSZFNMAX) {
		return;
	}
	height = hdl->height;
	cnv = cnvcpy[type];
	do {
		cnv(hdl, dst, src);
		src += salign;
		dst += dalign;
	} while(--height);
}

static RSZHDL fastcopy(int width, int height) {

	RSZHDL	ret;

	ret = (RSZHDL)_MALLOC(sizeof(_RSZHDL), "fastcopy");
	if (ret) {
		ret->func = fastcopyfunc;
		ret->width = width;
		ret->height = height;
	}
	return(ret);
}
#endif


// ---- area average

#if defined(RESIZE_AREAAVG)
static void aamix8(RSZEX hdl, const BYTE *src, int volume) {

	UINT	*buf;
	UINT	posx;
	int		i;
	UINT	curx;
	int		vol;

	buf = hdl->buf;
	posx = 0;
	for (i=0; i<hdl->orgx; i++) {
		curx = hdl->h[i];
		while((curx ^ posx) >> MXBITS) {
			vol = (int)(MULTIX - (posx & MXMASK)) * volume;
			buf[0] += src[0] * vol;
			buf += 1;
			posx &= ~MXMASK;
			posx += MULTIX;
		}
		curx -= posx;
		if (curx) {
			posx += curx;
			vol = (int)curx * volume;
			buf[0] += src[0] * vol;
		}
		src++;
	}
}

static void aamix16(RSZEX hdl, const BYTE *src, int volume) {

	UINT	*buf;
	UINT	posx;
	int		i;
	UINT	col;
	int		r, g, b;
	UINT	curx;
	int		vol;

	buf = hdl->buf;
	posx = 0;
	for (i=0; i<hdl->orgx; i++) {
		col = *(UINT16 *)src;
		src += 2;
		b = (col >> B16SFT) & ((1 << B16BIT) - 1);
		g = (col >> G16SFT) & ((1 << G16BIT) - 1);
		r = (col >> R16SFT) & ((1 << R16BIT) - 1);
		curx = hdl->h[i];
		while((curx ^ posx) >> MXBITS) {
			vol = (int)(MULTIX - (posx & MXMASK)) * volume;
			buf[0] += b * vol;
			buf[1] += g * vol;
			buf[2] += r * vol;
			buf += 3;
			posx &= ~MXMASK;
			posx += MULTIX;
		}
		curx -= posx;
		if (curx) {
			posx += curx;
			vol = (int)curx * volume;
			buf[0] += b * vol;
			buf[1] += g * vol;
			buf[2] += r * vol;
		}
	}
}

static void aamix24(RSZEX hdl, const BYTE *src, int volume) {

	UINT	*buf;
	UINT	posx;
	int		i;
	UINT	curx;
	int		vol;

	buf = hdl->buf;
	posx = 0;
	for (i=0; i<hdl->orgx; i++) {
		curx = hdl->h[i];
		while((curx ^ posx) >> MXBITS) {
			vol = (int)(MULTIX - (posx & MXMASK)) * volume;
			buf[0] += (int)src[0] * vol;
			buf[1] += (int)src[1] * vol;
			buf[2] += (int)src[2] * vol;
			buf += 3;
			posx &= ~MXMASK;
			posx += MULTIX;
		}
		curx -= posx;
		if (curx) {
			posx += curx;
			vol = (int)curx * volume;
			buf[0] += (int)src[0] * vol;
			buf[1] += (int)src[1] * vol;
			buf[2] += (int)src[2] * vol;
		}
		src += 3;
	}
}

static void aaout8(RSZEX hdl, BYTE *dst) {

const UINT	*buf;
	int		rem;

	buf = hdl->buf;
	rem = hdl->width;
	do {
		*dst++ = (BYTE)((*buf++) >> (MXBITS + MYBITS));
	} while(--rem);
}

static void aaout16(RSZEX hdl, BYTE *dst) {

const UINT	*buf;
	int		rem;
	UINT	tmp;
	UINT	col;

	buf = hdl->buf;
	rem = hdl->width;
	do {
		tmp = buf[0] + (buf[0] >> (8 - B16BIT));
		col = (tmp >> (MXBITS + MYBITS - B16SFT)) &
											(((1 << B16BIT) - 1) << B16SFT);
		tmp = buf[1] + (buf[1] >> (8 - G16BIT));
		col += (tmp >> (MXBITS + MYBITS - G16SFT)) &
											(((1 << G16BIT) - 1) << G16SFT);
		tmp = buf[2] + (buf[2] >> (8 - R16BIT));
		col += (tmp >> (MXBITS + MYBITS - R16SFT)) &
											(((1 << R16BIT) - 1) << R16SFT);
		*(UINT16 *)dst = (UINT16)col;
		dst += 2;
		buf += 3;
	} while(--rem);
}

static void aaout24(RSZEX hdl, BYTE *dst) {

const UINT	*buf;
	int		rem;

	buf = hdl->buf;
	rem = hdl->width * 3;
	do {
		*dst++ = (BYTE)((*buf++) >> (MXBITS + MYBITS));
	} while(--rem);
}

static void aaout16by24(RSZEX hdl, BYTE *dst) {

const UINT	*buf;
	int		rem;
	UINT	col;

	buf = hdl->buf;
	rem = hdl->width;
	do {
		col = (buf[0] >> (MXBITS + MYBITS + 8 - B16BIT - B16SFT)) &
											(((1 << B16BIT) - 1) << B16SFT);
		col += (buf[1] >> (MXBITS + MYBITS + 8 - G16BIT - G16SFT)) &
											(((1 << G16BIT) - 1) << G16SFT);
		col += (buf[2] >> (MXBITS + MYBITS + 8 - R16BIT - R16SFT)) &
											(((1 << R16BIT) - 1) << R16SFT);
		*(UINT16 *)dst = (UINT16)col;
		dst += 2;
		buf += 3;
	} while(--rem);
}

static void aaout24by16(RSZEX hdl, BYTE *dst) {

const UINT	*buf;
	int		rem;

	buf = hdl->buf;
	rem = hdl->width;
	do {
		dst[0] = (BYTE)(buf[0] >> (MXBITS + MYBITS - 8 + B16BIT));
		dst[1] = (BYTE)(buf[1] >> (MXBITS + MYBITS - 8 + G16BIT));
		dst[2] = (BYTE)(buf[2] >> (MXBITS + MYBITS - 8 + R16BIT));
		dst += 3;
		buf += 3;
	} while(--rem);
}

typedef void (*AAMIX)(RSZEX hdl, const BYTE *src, int volume);
typedef void (*AAOUT)(RSZEX hdl, BYTE *dst);

static const AAMIX fnaamix[RSZFNMAX] = {aamix8, aamix16, aamix24,
										aamix24, aamix16};
static const AAOUT fnaaout[RSZFNMAX] = {aaout8, aaout16, aaout24,
										aaout16by24, aaout24by16};

static void areaavefunc(RSZEX hdl, UINT type, BYTE *dst, int dalign,
												const BYTE *src, int salign) {

	AAMIX	aamix;
	AAOUT	aaout;
	UINT	posy;
	UINT	i;
	UINT	cury;

	if (type >= RSZFNMAX) {
		return;
	}
	aamix = fnaamix[type];
	aaout = fnaaout[type];
	ZeroMemory(hdl->buf, hdl->bufsize);
	posy = 0;
	for (i=0; i<(UINT)hdl->orgy; i++) {
		cury = (((i + 1) << MYBITS) * (UINT)hdl->height) / (UINT)hdl->orgy;
		while((cury ^ posy) >> MYBITS) {
			aamix(hdl, src, MULTIY - (posy & MYMASK));
			aaout(hdl, dst);
			dst += dalign;
			ZeroMemory(hdl->buf, hdl->bufsize);
			posy &= ~MYMASK;
			posy += MULTIY;
		}
		cury -= posy;
		if (cury) {
			posy += cury;
			aamix(hdl, src, cury);
		}
		src += salign;
	}
}

static RSZHDL areaave(int width, int height, int orgx, int orgy) {

	UINT	dstcnt;
	UINT	tbls;
	RSZEX	rszex;
	UINT	*ptr;
	UINT	i;

	dstcnt = width * 3;
	tbls = orgx + orgy + dstcnt;
	rszex = (RSZEX)_MALLOC(sizeof(_RSZEX) + (tbls * sizeof(UINT)), "RSZEX");
	if (rszex) {
		rszex->func = (RSZFN)areaavefunc;
		rszex->width = width;
		rszex->height = height;
		rszex->orgx = orgx;
		rszex->orgy = orgy;
		ptr = (UINT *)(rszex + 1);
		rszex->h = ptr;
		i = 0;
		do {
			i++;
			*ptr++ = ((i << MXBITS) * (UINT)width) / (UINT)orgx;
		} while(i < (UINT)orgx);
		rszex->buf = ptr;
		rszex->bufsize = dstcnt * sizeof(UINT);
	}
	return((RSZHDL)rszex);
}
#endif


// ----

UINT resize_gettype(int dbpp, int sbpp) {

	UINT	ret;

	if (dbpp == 8) {
		ret = RSZFN_8BPP;
	}
	else if (dbpp == 16) {
		ret = (sbpp == 24)?RSZFN_16BY24:RSZFN_16BPP;
	}
	else if (dbpp == 24) {
		ret = (sbpp == 16)?RSZFN_24BY16:RSZFN_24BPP;
	}
	else {
		ret = RSZFNMAX;
	}
	if ((dbpp != sbpp) && (ret < RSZFN_16BY24)) {
		ret = RSZFNMAX;
	}
	return(ret);
}

RSZHDL resize(int xdst, int ydst, int xsrc, int ysrc) {

	UINT	flag;

	flag = 0;
	if ((xsrc <= 0) || (xdst <= 0)) {
		goto rsz_err;
	}
	if (xsrc != xdst) {
		flag += RESIZE_HDIF;
	}
	if ((ysrc <= 0) || (ydst <= 0)) {
		goto rsz_err;
	}
	if (ysrc != ydst) {
		flag += RESIZE_VDIF;
	}
#if defined(RESIZE_FASTCOPY)
	if (flag == 0) {
		return(fastcopy(xdst, ydst));
	}
#endif
#if defined(RESIZE_AREAAVG)
	return(areaave(xdst, ydst, xsrc, ysrc));
#endif

rsz_err:
	return(NULL);
}

